class Generator {
  constructor() {}

  // 获取数独矩阵a*a
  getresult(a) {
    let l = a
    // 初始化二维数组
    let n = []
    for (let i = 0; i < l; i++) {
      n[i] = []
      for (let j = 0; j < l; j++) {
        n[i][j] = 0
      }
    }
    // 生成数字
    for (let i = 0; i < l; i++) {
      // 尝试填充的数字次数
      let time = 0
      // 填充数字
      for (let j = 0; j < l; j++) {
        // 产生数字
        n[i][j] = this.generateNum(time, l)
        // 如果返回值为0，则代表卡住，退回处理
        // 退回处理的原则是：如果不是第一列，则先倒退到前一列，否则倒退到前一行的最后一列
        if (n[i][j] == 0) {
          // 不是第一列，则倒退一列
          if (j > 0) {
            j -= 2
            continue
          } else {
            // 是第一列，则倒退到上一行的最后一列
            i--
            j = l - 1
            continue
          }
        }
        // 填充成功
        if (this.isCorret(i, j, n)) {
          // 初始化time，为下一次填充做准备
          time = 0
        } else {
          // 继续填充
          // 次数增加1
          time++
          // 继续填充当前格
          j--
        }
      }
    }

    // 返回结果
    return n
  }

  /**
   * @desc 是否满足行、列不重复的要求
   * @param row   行号
   * @param col   列号
   * @param n   二维数组
   * @return true代表符合要求
   */
  isCorret(row, col, n) {
    return this.checkRow(row, n) & this.checkLine(col, n)
  }

  /**
   * @desc 检查行是否符合要求
   * @param row   检查的行号
   * @param n   二维数组
   * @return true代表符合要求
   */
  checkRow(row, n) {
    let l = n.length
    for (let j = 0; j < l - 1; j++) {
      if (n[row][j] == 0) {
        continue
      }
      for (let k = j + 1; k < l; k++) {
        if (n[row][j] == n[row][k]) {
          return false
        }
      }
    }
    return true
  }

  /**
   * @desc 检查列是否符合要求
   * @param col   检查的列号
   * @return true代表符合要求
   */
  checkLine(col, n) {
    let l = n.length
    for (let j = 0; j < l - 1; j++) {
      if (n[j][col] == 0) {
        continue
      }
      for (let k = j + 1; k < l; k++) {
        if (n[j][col] == n[k][col]) {
          return false
        }
      }
    }
    return true
  }

  /**
   * @desc 产生1-l之间的随机数字 规则：生成的随机数字放置在数组l-1-time下标的位置，随着time的增加，已经尝试过的数字将不会在取到
   * 说明：即第一次次是从所有数字中随机，第二次时从前八个数字中随机，依次类推， 这样既保证随机，也不会再重复取已经不符合要求的数字，提高程序的效率
   * 这个规则是本算法的核心
   * @param time  填充的次数，0代表第一次填充
   * @return
   */
  generateNum(time, l) {
    let num = []
    for (let t = 1; t <= l; t++) {
      num[t - 1] = t
    }
    // 第一次尝试时，初始化随机数字源数组
    if (time == 0) {
      for (let i = 0; i < l; i++) {
        num[i] = i + 1
      }
    }
    // 第l+1次填充，表明该位置已经卡住，则返回0，由主程序处理退回
    if (time == l) {
      return 0
    }
    // 不是第一次填充
    // 生成随机数字，该数字是数组的下标，取数组num中该下标对应的数字为随机数字
    let ranNum = Math.floor(Math.random() * (l - time))
    // 把数字放置在数组倒数第time个位置，
    let temp = num[l - 1 - time]
    num[l - 1 - time] = num[ranNum]
    num[ranNum] = temp
    // 返回数字
    return num[l - 1 - time]
  }

  // 划分牢笼
  flaggroup(a) {
    // 初始化二维数组
    let tem = []
    for (let i = 0; i < a; i++) {
      tem[i] = []
      for (let j = 0; j < a; j++) {
        tem[i][j] = 0
      }
    }
    let count = 1

    for (let i = 0; i < a; i++) {
      for (let j = 0; j < a; j++) {
        if (tem[i][j] == 0) {
          // 随机格子数
          let max = 1
          if (a > 6) {
            // 3、4
            max = Math.round(Math.random() + 3)
          } else {
            if (a > 4) {
              // 2、3
              max = Math.round(Math.random() + 2)
            } else {
              if (a > 2) {
                max = 2
              } else {
                max = 1
              }
            }
          }

          // 结束随机大小
          // 标记
          tem = this.fillvalue(i, j, tem, count, max)
          count++
          // 结束标记
        }
      }
    }

    //检测防止重复
    if (!this.checkmatrix(tem)) {
      tem = this.flaggroup(a)
    }

    return tem
  }

  //检测牢笼，避免出现对称现象,返回true为验证通过
  checkmatrix(input) {
    let result = true
    let l = input.length

    for (let m = 0; m < l - 1; m++) {
      if (this.checkij(m, m + 1, input, true)) {
        //有相等的两相邻行
        result = false
        break
      }
      if (this.checkij(m, m + 1, input, false)) {
        //有相等的两相邻列
        result = false
        break
      }
    }

    return result
  }

  //检测第i,j行列,types为true时为行,返回true为相等
  checkij(i, j, input, types) {
    let l = input.length
    let temr = true

    for (let m = 0; m < l; m++) {
      if (types) {
        if (input[i][m] != input[j][m]) {
          temr = false
          break
        }
      } else {
        if (input[m][i] != input[m][j]) {
          temr = false
          break
        }
      }
    }
    return temr
  }

  // 指定位置标记值,划分算法核心，构造递归
  // @row 指定的行
  // @col 指定的列
  // @input 输入的矩阵
  // @mvalue要填充的值
  // @maxnum 最大多少格子连在一起
  fillvalue(row, col, input, mvalue, maxnum) {
    let l = input.length

    if (maxnum > 0 && input[row][col] == 0) {
      input[row][col] = mvalue
      maxnum--
      let mr = Math.round(Math.random() + 1) // 1或者2随机数

      if (mr == 1) {
        // 若为1则向右
        col++
        if (col >= 0 && col < l) {
          if (input[row][col] == 0) {
            input = this.fillvalue(row, col, input, mvalue, maxnum)
          }
        }

        if (col >= l) {
          col--
          row++
          if (row < l) {
            input = this.fillvalue(row, col, input, mvalue, maxnum)
          }
        }
      }

      if (mr == 2) {
        // 若为2向下
        row++
        if (row >= 0 && row < l) {
          if (input[row][col] == 0) {
            input = this.fillvalue(row, col, input, mvalue, maxnum)
          }
        }

        if (row >= l) {
          row--
          col++
          if (col < l) {
            input = this.fillvalue(row, col, input, mvalue, maxnum)
          }
        }
      }
    }

    return input
  }

  // 获取符号,@input是经过圈后处理过的符号规则
  getsignal(input) {
    let result = ''

    let l = input.length

    let max = 0

    for (let i = 0; i < l; i++) {
      for (let j = 0; j < l; j++) {
        if (input[i][j] > max) {
          max = input[i][j]
        }
      }
    }
    let mystore = new Array(max)

    for (let i = 0; i < max; i++) {
      mystore[i] = '$'
    }

    for (let i = 0; i < l; i++) {
      for (let j = 0; j < l; j++) {
        let tem = input[i][j]
        mystore[tem - 1] = mystore[tem - 1] + '|' + i + '-' + j
      }
    }

    for (let i = 0; i < max; i++) {
      result = result + mystore[i]
    }
    return result.replace('$|', '$').substring(1)
  }

  // 确定运算符号以及结果
  // @input为数独矩阵
  // @getsignal为待处理的字符牢笼
  getsignalaction(input, getsignal) {
    let result = '$'
    let getfenarr = getsignal.split('$|') // 各大组分解
    for (let i = 0; i < getfenarr.length; i++) {
      let temps = getfenarr[i].split('|')

      if (temps.length == 1) {
        // 没有相邻
        let point = temps[0].split('-')
        let x = parseInt(point[0])
        let y = parseInt(point[1])
        result = result + '$' + input[x][y] + '|' + '=' + '|' + x + '-' + y
      }

      if (temps.length == 2) {
        // 两个相邻

        let point1 = temps[0].split('-')
        let x1 = parseInt(point1[0])
        let y1 = parseInt(point1[1])

        let point2 = temps[1].split('-')
        let x2 = parseInt(point2[0])
        let y2 = parseInt(point2[1])
        let x = 0
        let y = 0

        if (y1 < y2) {
          x = x1
          y = y1
        }

        if (y1 == y2) {
          if (x1 < x2) {
            x = x1
            y = y1
          } else {
            x = x2
            y = y2
          }
        }

        if (y1 > y2) {
          x = x2
          y = y2
        }

        let mr = Math.round(Math.random() * 3 + 1) // 1-4随机数

        if (mr == 1) {
          result =
            result +
            '$' +
            (input[x1][y1] + input[x2][y2]) +
            '|' +
            '+' +
            '|' +
            x +
            '-' +
            y
        }

        if (mr == 2) {
          result =
            result +
            '$' +
            Math.abs(input[x1][y1] - input[x2][y2]) +
            '|' +
            '-' +
            '|' +
            x +
            '-' +
            y
        }

        if (mr == 3) {
          result =
            result +
            '$' +
            input[x1][y1] * input[x2][y2] +
            '|' +
            '×' +
            '|' +
            x +
            '-' +
            y
        }

        if (mr == 4) {
          let mydiv = -1
          if (input[x1][y1] % input[x2][y2] == 0) {
            mydiv = input[x1][y1] / input[x2][y2]
          }

          if (input[x2][y2] % input[x1][y1] == 0) {
            mydiv = input[x2][y2] / input[x1][y1]
          }

          if (mydiv > 0) {
            result = result + '$' + mydiv + '|' + '÷' + '|' + x + '-' + y
          } else {
            result =
              result +
              '$' +
              (input[x1][y1] + input[x2][y2]) +
              '|' +
              '+' +
              '|' +
              x +
              '-' +
              y
          }
        }
      }

      if (temps.length == 3) {
        // 三个相邻
        let point1 = temps[0].split('-')
        let x1 = parseInt(point1[0])
        let y1 = parseInt(point1[1])

        let point2 = temps[1].split('-')
        let x2 = parseInt(point2[0])
        let y2 = parseInt(point2[1])

        let point3 = temps[2].split('-')
        let x3 = parseInt(point3[0])
        let y3 = parseInt(point3[1])

        let mr = Math.round(Math.random() + 1) // 1-2随机数

        let x = 0
        let y = 0

        if (y1 < y2) {
          x = x1
          y = y1
        }

        if (y1 == y2) {
          if (x1 < x2) {
            x = x1
            y = y1
          } else {
            x = x2
            y = y2
          }
        }

        if (y1 > y2) {
          x = x2
          y = y2
        }

        if (y > y3) {
          y = y3
          x = x3
        }

        if (y == y3) {
          if (x > x3) {
            x = x3
            y = y3
          }
        }

        if (mr == 1) {
          result =
            result +
            '$' +
            (input[x1][y1] + input[x2][y2] + input[x3][y3]) +
            '|' +
            '+' +
            '|' +
            x +
            '-' +
            y
        } else {
          result =
            result +
            '$' +
            input[x1][y1] * input[x2][y2] * input[x3][y3] +
            '|' +
            '×' +
            '|' +
            x +
            '-' +
            y
        }
      }
      //3完成

      if (temps.length == 4) {
        // 四个相邻
        let point1 = temps[0].split('-')
        let x1 = parseInt(point1[0])
        let y1 = parseInt(point1[1])

        let point2 = temps[1].split('-')
        let x2 = parseInt(point2[0])
        let y2 = parseInt(point2[1])

        let point3 = temps[2].split('-')
        let x3 = parseInt(point3[0])
        let y3 = parseInt(point3[1])

        let point4 = temps[3].split('-')
        let x4 = parseInt(point4[0])
        let y4 = parseInt(point4[1])

        let mr = Math.round(Math.random() + 1) // 1-2随机数

        let x = 0
        let y = 0

        if (y1 < y2) {
          x = x1
          y = y1
        }

        if (y1 == y2) {
          if (x1 < x2) {
            x = x1
            y = y1
          } else {
            x = x2
            y = y2
          }
        }

        if (y1 > y2) {
          x = x2
          y = y2
        }

        if (y > y3) {
          y = y3
          x = x3
        }

        if (y == y3) {
          if (x > x3) {
            x = x3
            y = y3
          }
        }

        if (y > y4) {
          y = y4
          x = x4
        }

        if (y == y4) {
          if (x > x4) {
            x = x4
            y = y4
          }
        }

        if (mr == 1) {
          result =
            result +
            '$' +
            (input[x1][y1] + input[x2][y2] + input[x3][y3] + input[x4][y4]) +
            '|' +
            '+' +
            '|' +
            x +
            '-' +
            y
        } else {
          result =
            result +
            '$' +
            input[x1][y1] * input[x2][y2] * input[x3][y3] * input[x4][y4] +
            '|' +
            '×' +
            '|' +
            x +
            '-' +
            y
        }
      }
    }
    return result.replace('$$', '')
  }
}

// 添加a*a聪明方格问题到数据库中
function addNewQuestion(a) {
  let sudoku = new Generator()
  let sd = sudoku.getresult(a) // 数独矩阵
  let answer = '|'
  let n = sudoku.flaggroup(a) // 牢笼矩阵
  let cage = '|'
  let figure = sudoku.getsignalaction(sd, sudoku.getsignal(n)) // 符号字符串

  for (let i = 0; i < sd.length; i++) {
    for (let j = 0; j < sd.length; j++) {
      answer = answer + '|' + sd[i][j]
    }
  }

  for (let i = 0; i < n.length; i++) {
    for (let j = 0; j < n.length; j++) {
      cage = cage + '|' + n[i][j]
    }
  }

  answer = answer.replace('||', '') // 数独
  cage = cage.replace('||', '') // 牢笼

  return {
    answer,
    cage,
    figure
  }
}

/**
 * 检测行列重复
 * @param {*} grids
 */
function checkRowCol(grids) {
  let dim = Math.sqrt(grids.length)
  let res = true
  let num = 0

  // 遍历所有元素
  for (let i = 0; i < grids.length; ++i) {
    // 横向对比
    let rowFirst = Math.floor(i / dim) * dim
    let j = rowFirst
    num = 0

    while (num < dim) {
      // console.log(`${j}:${grids[j].userAnswer}----${i}:${grids[i].userAnswer}`)
      if (j != i && grids[j].userAnswer == grids[i].userAnswer) {
        res = false
        break
      }
      ++num
      ++j
    }
    if (!res) {
      break
    }

    // 竖向对比
    let colFirst = i % dim
    let k = colFirst
    num = 0

    while (num < dim) {
      // console.log(`${k}:${grids[k].userAnswer}----${i}:${grids[i].userAnswer}`)
      if (k != i && grids[k].userAnswer == grids[i].userAnswer) {
        res = false
        break
      }
      ++num
      k += dim
    }
    if (!res) {
      break
    }
  }

  // console.log(res)
  return res
}

/**
 * 检测牢笼
 * @param {*} grids
 */
function checkCage(grids) {
  let dim = Math.sqrt(grids.length)
  let res = true
  let cagesConfig = {}

  grids.forEach(g => {
    if (g.number && g.symbol) {
      cagesConfig[g.cage] = {
        number: g.number,
        symbol: g.symbol,
        value: g.userAnswer
      }
    } else {
      let symbol = cagesConfig[g.cage].symbol
      let value = cagesConfig[g.cage].value

      if (symbol == '+') {
        value += g.userAnswer
      }

      if (symbol == '×') {
        value *= g.userAnswer
      }

      if (symbol == '-') {
        value = Math.abs(value - g.userAnswer)
      }

      if (symbol == '÷') {
        if (value / g.userAnswer > 1) {
          value = value / g.userAnswer
        } else {
          value = g.userAnswer / value
        }
      }
      cagesConfig[g.cage].value = value
      // console.log(cagesConfig[g.cage])
    }
  })

  for (let key in cagesConfig) {
    let cageConfig = cagesConfig[key]
    if (cageConfig.number != cageConfig.value) {
      res = false
      break
    }
  }
  // console.log(res)

  return res
}

module.exports = {
  addNewQuestion,
  checkRowCol,
  checkCage
}
